#include "my_pool.h"

static void task_queue_init(POOL *pool, int task_queue_max)
{

	pool->front = pool->rear = (TASK *)malloc(sizeof (TASK));

	if (pool->front == NULL)
	{
		perror("Task queue init: malloc");
		exit(-1);
	}

	pool->front->callback_fun = NULL;
	pool->front->argv = NULL;
	pool->front->next = NULL;

	pool->task_queue_cur = 0;
	pool->task_queue_max = task_queue_max;

}

POOL *pool_init(int thread_max_num, int task_queue_max)
{
	/*init pointer of thread pool*/
	POOL *pool;
	pool = (POOL *)malloc(sizeof (POOL));

	if(pool == NULL)
	{
		perror("Thread pool init: malloc");
		exit(-1);
	}

	/*initialization task queue;*/
	task_queue_init(pool, task_queue_max);

	pool->thread_max_num = thread_max_num;

	/*init thread list*/
	pool->thread_list = (pthread_t *)malloc( pool->thread_max_num * sizeof(pthread_t));

	if (pool->thread_list == NULL)
	{
		perror("Thread pool init: thread_list: malloc");
		exit(1);
	}

	/*init mutex & condition*/
	pthread_mutex_init(&pool->task_queue_acess, NULL);
	pthread_cond_init(&pool->task_queue_not_empty, NULL);
	pthread_cond_init(&pool->task_queue_idle, NULL);

	/*create threads*/
	int i;
	for (i = 0; i < pool->thread_max_num; i++)
	{
		pthread_create(&pool->thread_list[i], NULL, thread_handle, (void *)pool);
	}

	return pool;

}

static void *thread_handle(void *argv)
{

	POOL *pool = (POOL *)argv;
	TASK *gtask = NULL; 
	TASK *done_task = NULL; 
	while(1)
	{

		pthread_mutex_lock(&pool->task_queue_acess);

		while (pool->task_queue_cur == EMPTY && pool->front->next == NULL)
		{
			/*if task queue EMPTY, waiting for task add*/
		//	printf("[ %lu ] waiting task!\n", pthread_self());

			/*唤醒后是否会自动加锁？？？？？？*/
			pthread_cond_wait(&pool->task_queue_not_empty, &pool->task_queue_acess);
		}

		gtask = pool->front->next;
		done_task = pool->front;
		pool->task_queue_cur--;
		free(done_task);
		done_task = NULL;
		pool->front = gtask;

		/*inform the thread of waiting for queue idle*/
		pthread_cond_broadcast(&pool->task_queue_idle);
		pthread_mutex_unlock(&pool->task_queue_acess);

		/*perform a task*/
		gtask->callback_fun(gtask->argv);

	}

}

int pool_add_task(POOL *pool,void *(*function)(void *argv), void *argv)
{

	pthread_mutex_lock(&pool->task_queue_acess);

	while (pool->task_queue_cur == pool->task_queue_max)
	{
		/*if task queue busy, waiting for task queue idle*/
		pthread_cond_wait(&pool->task_queue_idle, &pool->task_queue_acess);
	}
	pthread_mutex_unlock(&pool->task_queue_acess);

	/*create task node memory*/
	TASK *new_task = (TASK *)malloc(sizeof(TASK));

	if (new_task == NULL)
	{
		perror("Add task to the thread pool failed: malloc");
		pthread_mutex_unlock(&pool->task_queue_acess);
		return -1;
	}

	/*init new task struct variable*/
	new_task->callback_fun = function;
	new_task->argv = argv;
	new_task->next = pool->rear->next;

	pthread_mutex_lock(&pool->task_queue_acess);
	/*add new task into thread pool*/
	pool->rear->next = new_task;
	pool->rear = new_task;
	pool->task_queue_cur++;
	

	if (pool->task_queue_cur > 0 && pool->front->next != NULL)
	{
		pthread_cond_broadcast(&pool->task_queue_not_empty);
	}
	pthread_mutex_unlock(&pool->task_queue_acess);

	return 0;

}

void destroy_pool(POOL *pool)
{
	printf("destroy pool!\n");
}
